function DECODING_RESULTS = create_confusion_matrices(cv, YTe, all_predicted_labels, DECODING_RESULTS, all_cm_rank_results)
%   A help method to create confusion matrices for the zero-one loss and normalized rank results
%   A confusion matrix is a matrix in which the columns express the true class label
%   and the rows express the predicted label.  Thus, for example, if column 2, and row 3
%   had a value of 7, it would mean that there were 7 times in which class 2 was mistakenly
%   predicted to be class 3 (summing the columns will give the total number of examples for 
%   each class, which can be used to turn the confusion matrix into a misclassification probability
%   distribution).  

% confusion matrix => predicted_label x real_label x num_time_periods x (num_time_periods)
% I am assuming YTe is the same for all time periods, boostrap iterations and CV trials, which should always be met as far as I can tell)


%==========================================================================

%     This code is part of the Neural Decoding Toolbox.
%     Copyright (C) 2011 by Ethan Meyers (emeyers@mit.edu)
% 
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
% 
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
% 
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
%==========================================================================   



    real_labels = YTe;
    unique_labels = unique(YTe);

    % remap labels if they are not consequative positive numbers starting at 1
    if min(YTe) < 0 || ~(unique_labels(end) == length(unique_labels))

        old_all_predicted_labels = all_predicted_labels;
        for iBoot = 1:size(all_predicted_labels, 2)
            
            all_predicted_labels{iBoot} = zeros(size(old_all_predicted_labels{iBoot}));

            for iNewLabels = 1:length(unique_labels)
                
                all_predicted_labels{iBoot}(find(old_all_predicted_labels{iBoot} == unique_labels(iNewLabels))) = iNewLabels;
                real_labels(find(YTe == unique_labels(iNewLabels))) = iNewLabels;
                cf_label_remapping(iNewLabels) = unique_labels(iNewLabels);  % save this info b/c it might be useful later
            
            end            

            if length(find(all_predicted_labels{iBoot} == 0)) > 1
                warning('problems remapping labels, when creating the confusion matrix!!!!')
            end

        end
            
         DECODING_RESULTS.ZERO_ONE_LOSS_RESULTS.confusion_matrix_results.label_remapping = cf_label_remapping; 

    end

    

    num_labels = length(unique_labels);
    num_train_time_intervals = size(all_predicted_labels{1}, 2);   
    num_test_time_intervals = size(all_predicted_labels{1}, 3);   
    num_predicted_labels = size(all_predicted_labels{1}, 4);


    if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1   % make a smaller confusion matrix (to save disk space) if only saving confusion matrix at same training and test times
        confusion_matrix = zeros(num_labels, num_labels, num_train_time_intervals);
    else
        confusion_matrix = zeros(num_labels, num_labels, num_train_time_intervals, num_test_time_intervals);
    end

    if cv.confusion_matrix_params.create_all_test_points_separate_confusion_matrix == 1
        if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1
            all_test_points_separate_confusion_matrix = zeros(num_labels, length(real_labels), num_train_time_intervals);   % useful in the case when ID labels have been remapped, yet still interested in ID original prediction
        else
            all_test_points_separate_confusion_matrix = zeros(num_labels, length(real_labels), num_train_time_intervals, num_test_time_intervals);   % useful in the case when ID labels have been remapped, yet still interested in ID original prediction
        end
    end

    

    for iTrain = 1:num_train_time_intervals

        if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1 && (num_train_time_intervals == num_test_time_intervals)  
            iTest_range = iTrain;
        else
            iTest_range = 1:num_test_time_intervals;
        end

        for iTest = iTest_range

            for iCV = 1:size(all_predicted_labels{1}, 1)
                for iBoot = 1:size(all_predicted_labels, 2)

                    % put entry into into the confusion matrix:  
                    for iPredlabels = 1:num_predicted_labels


                        % make a smaller confusion matrix (to save disk space) if only saving confusion matrix at same training and test times
                        if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1
                             confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), real_labels(iPredlabels), iTrain)...
                                = confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), real_labels(iPredlabels), iTrain) + 1;
                        else                                    
                            confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), real_labels(iPredlabels), iTrain, iTest)...
                                = confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), real_labels(iPredlabels), iTrain, iTest) + 1;
                        end


                        if cv.confusion_matrix_params.create_all_test_points_separate_confusion_matrix == 1

                            if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1
                                 all_test_points_separate_confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), iPredlabels, iTrain)...
                                    = all_test_points_separate_confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), iPredlabels, iTrain) + 1; 
                            else
                                all_test_points_separate_confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), iPredlabels, iTrain, iTest)...
                                    = all_test_points_separate_confusion_matrix(all_predicted_labels{iBoot}(iCV, iTrain, iTest, iPredlabels), iPredlabels, iTrain, iTest) + 1;
                            end

                        end


                    end


                end
            end


        end
    end



    if cv.confusion_matrix_params.create_confusion_matrix == 1
        DECODING_RESULTS.ZERO_ONE_LOSS_RESULTS.confusion_matrix_results.confusion_matrix = confusion_matrix;
    end

    if cv.confusion_matrix_params.create_all_test_points_separate_confusion_matrix == 1
        DECODING_RESULTS.ZERO_ONE_LOSS_RESULTS.confusion_matrix_results.all_test_points_separate_confusion_matrix = all_test_points_separate_confusion_matrix;   % might be a bit memory intensive though
    end

    
    
    % create rank confusion matrix 
    if cv.save_results.normalized_rank == 1

            DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix = zeros(size(all_cm_rank_results{1})); 
            for iBoot = 1:size(all_cm_rank_results, 2)  
                DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix = DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix + ...
                    1 - ((all_cm_rank_results{iBoot} - 1)./(DECODING_RESULTS.BOOTSTRAP_CV_PARAMETERS.num_unique_labels-1));  
            end
            DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix = DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix./size(all_cm_rank_results, 2);   
         
            
            DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix_label_mapping = unique(YTe);
    end
    
    
    
    % shift the dimensions to get the confusion matrix if the form [num_pred x num_actual x num_train_times x (num_test_times)]
    if cv.confusion_matrix_params.save_confusion_matrix_only_train_and_test_at_same_time == 1
        DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix = shiftdim(DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix, 1);
    else
        DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix = shiftdim(DECODING_RESULTS.NORMALIZED_RANK_RESULTS.confusion_matrix_results.rank_confusion_matrix, 2);
    end
        
        
    
    
    
    
    
    
    















